package com.isecommerce.application.datawizard;

import java.io.*;


import com.isecommerce.framework.util.*;
import com.isecommerce.framework.entity.*;
import com.isecommerce.application.datawizard.entity.*;

public class DataWizardMain {
	
	private String configPath; 
	private Config impConfig;
	private RAWCSVFileInfoManager impRAWCSVM;
	private OutputCSVFileInfoManager impOutputCSVM;
	private StandardFormatInfoManager impStdFormatM;
	
	private LogWriter logger;
   
    public DataWizardMain(String configPath){
    	
    	// Temporary Preset
    	this.configPath = configPath;    	
    	
    	// INITIALIZING
    	initConfig();
    }
    
    // Init
    private void initConfig(){

    	// Init logger
    	this.logger = LogWriter.getLogWriterInstance("DATA_WIZARD", LogWriter.DEBUG, new PrintWriter(System.out));
    	this.logger.setOwner(this.getClass().getSimpleName());
    	
    	printSepLine(LogWriter.INFO);
    	    	
    	this.logger.log("[INIT CONFIG]", LogWriter.INFO);
    	
    	impConfig = new Config();
    	impConfig.webInit(configPath);
    	
    	this.logger.log("[INIT STANDARD FORMAT META INFO]", LogWriter.INFO);
    	impStdFormatM = new StandardFormatInfoManager();
    	impStdFormatM.init(impConfig);   
    	
    	// Reset log level with configured log level
    	this.logger.setLogLevel(impStdFormatM.getCSVfileInfo().logLevel);
    	this.logger.log("[SET LOG LEVEL] " + this.logger.getLogLevel(), LogWriter.INFO);
    	
    	this.logger.log("[INIT RAW CSV FILE META INFO]", LogWriter.INFO);
    	impRAWCSVM = new RAWCSVFileInfoManager();
    	impRAWCSVM.init(impConfig);
    	
    	this.logger.log("[INIT OUTPUT CSV FILE META INFO]", LogWriter.INFO);
    	impOutputCSVM = new OutputCSVFileInfoManager();
    	impOutputCSVM.init(impConfig);
    	
    	// Get current class name
    	this.logger.setOwner(this.getClass().getSimpleName());
    	    	
    }    
    
    // Main
    public void execute(){
    	
    	this.logger.log("\n[EXECUTE RAW CSV IMPORT OPERATION]", LogWriter.INFO);
    	
    	// Get CSV file count
    	int csvCnt = impRAWCSVM.getFileCount();
    	
    	// Temp entity
    	RAWCSVFileInfoEntity fileInfo = null;
    	BufferedReader csvFile = null;    	  	
    	BufferedWriter stdFile = null;
    	    	    	    	    	    	   	
    	// Load CSV file(loop by csv.count)
    	for(int i=0; i < csvCnt; i++){
    		
    		// Get CSV file info
    		fileInfo = impRAWCSVM.getCSVfileInfoByIndex(i);
    		
    		// Process only enabled CSV info
    		if(fileInfo.isEnabled){
    		
    			printSepLine(LogWriter.INFO);
    			this.logger.log("[RAW CSV] : " + fileInfo.csvName, LogWriter.INFO);
    		
    			try{
    			
    				// Read CSV file with given encode value
    				csvFile = new BufferedReader(new InputStreamReader(new FileInputStream(fileInfo.fileName),fileInfo.encode));
    				stdFile = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileInfo.fileName+this.impStdFormatM.getCSVfileInfo().postfix),fileInfo.encode));
    				
    				this.logger.log("[LOAD FILE] " + fileInfo.fileName, LogWriter.INFO);
                       		
    				// Run standardize process
    				this.standardization(csvFile, stdFile, fileInfo);
    				
    				this.logger.log("[COMPLETE RAW CSV IMPORT OPERATION]", LogWriter.INFO);
    				
    				this.printSepLine(LogWriter.INFO);
        		
    			}catch(IOException ioe){
    				this.logger.log("[CSV_FILE_ERR] -> " + ioe.toString(), LogWriter.ERROR);
    			}catch(Exception e){
    				this.logger.log("[ILLEGAL_EXCEPTION] -> " + e.toString(), LogWriter.ERROR);
    			}finally{    	
    				try{
    					if(csvFile != null) csvFile.close();    				
    				}catch(Exception e){    				
    				}
    				
    				try{
    					if(stdFile != null) stdFile.close();    				
    				}catch(Exception e){    				
    				}
    				
    			}
    		
    		}
    		
    		// Generate output file
    		this.logger.log("[FORMAT GENERATOR] " + this.GetGeneratorClassName(this.impOutputCSVM.getMainClassName()), LogWriter.DEBUG);
    		
			try{
    		
				ClassLoader myClassLoader = ClassLoader.getSystemClassLoader();
				FormatGenerator subGen = (FormatGenerator) myClassLoader.loadClass(this.GetGeneratorClassName(this.impOutputCSVM.getMainClassName())).newInstance();
				subGen.generate(this.impStdFormatM, this.impRAWCSVM, this.impOutputCSVM);
				
				this.logger.setOwner(this.getClass().getSimpleName());
				
			}catch(ClassNotFoundException fe){
				this.logger.log("[FORMAT GENERATOR CLASS NOT FOUND]", LogWriter.ERROR);
			}catch(Exception e){
				this.logger.log(e.toString(), LogWriter.ERROR);
			}
			
    		
    	        			
    	}
    	
    	
    }
    
    private String GetGeneratorClassName(String className){
    	return "com.isecommerce.application.datawizard.dao." + className;
    }
    
    
    // Standarize each RAW CSV file with given raw csv and output file name.
    private void standardization(BufferedReader csvFile, BufferedWriter outputFile, RAWCSVFileInfoEntity rawFileInfo) throws IOException{
    	
    	int skipCount = 0;
    	String nextLine = null;
    	StringBuffer aggString = new StringBuffer(""); 
		 
		while((nextLine = csvFile.readLine()) != null){
			
			// Skip Line check    					
			if(skipCount > rawFileInfo.skipLine){
				
				// Step 1 : Remove empty row
				if(!isEmptyRow(nextLine, rawFileInfo.colDel)){
					
					// End of Line String check and switching
					if(rawFileInfo.endOfLine.length() != 0){
						
						// Step 2 : Handle one row with linefeed data.											
						
						// Not aggregated and end with endOfLine string
						if(nextLine.endsWith(rawFileInfo.endOfLine)){
							
							this.logger.log("[AGGREGATE_F] " + nextLine, LogWriter.DEBUG);
							
							// Add aggregate mark before aggregate
							if(aggString.length() > 0) aggString.append(this.impStdFormatM.getCSVfileInfo().aggregateMark);
							
							aggString.append(nextLine);
							
							
							// Step 3 : Change column data with defined rule and add to output file														
							outputFile.write(this.generateStandardCSV(aggString.toString(), rawFileInfo, this.impStdFormatM.getCSVfileInfo()));
							
							outputFile.newLine();
							
							this.logger.log("[FINALIZE ONE ROW]", LogWriter.DEBUG);
							
							// clear string buffer
							aggString.delete(0, aggString.length());
							
						}else {
							
							this.logger.log("[AGGREGATE] " + nextLine, LogWriter.DEBUG);
							
							// Add aggregate mark before aggregate
							if(aggString.length() > 0) aggString.append(this.impStdFormatM.getCSVfileInfo().aggregateMark);
							
							aggString.append(nextLine);
							
						}
						
						
					}else {
						
						// Generate standard CSV formatted data
						outputFile.write(nextLine);
						outputFile.newLine();
						
					}
					
					
				}
				
			}else skipCount++;    					
		}
    	
    }
    
    private String generateStandardCSV(String text, RAWCSVFileInfoEntity rawFileInfo, StandardFormatInfoEntity stdFormatInfo){
    	    	
    	
    	CSVEntity csvE = new CSVEntity(text, rawFileInfo.colDel);    	
    	RAWCSVColumnInfoEntity colInfo = null;
    	
    	StringBuffer strBuf = new StringBuffer();
    	String column = null;
    	
    	this.logger.log("[GENERATE STANDARD CSV]", LogWriter.INFO);
    	
    	for(int i=0; i<csvE.getItemCount(); i++){
    		
    		// Get column meta info
    		colInfo = rawFileInfo.colInfoArray.get(i);
    		
    		// Apply rule with given column meta info
    		column = this.applyRAWCSVColumnRule(csvE.getItemAt(i), colInfo);
    		    		
    		
    		//Append to string buffer
    		if(strBuf.length() > 0) strBuf.append(stdFormatInfo.colDel);
    		strBuf.append(column);   		
    		
    		
    	}
    	
    	
    	return strBuf.toString();
    }
    
    private String applyRAWCSVColumnRule(String sourceText, RAWCSVColumnInfoEntity colInfo){
    	
    	
    	String MULTIPLE_REPLACE_ITEM_DELIMITER = "\t";
    	String REPLACE_ITEM_DELIMITER = "::";
    	
    	// Applying column rule by given column meta info    	
    	this.logger.log("[COLUMN_NAME] " + colInfo.colName, LogWriter.DEBUG);
    	this.logger.log("[COLUMN_VALUE]" + sourceText, LogWriter.DEBUG);
    	
    	// RULE : TRIM 
    	if(colInfo.isTrim){
    		sourceText = sourceText.trim();
    		this.logger.log("[TRIM_TEXT]", LogWriter.DEBUG);
    		
    	}
    	
    	// RULE : Remove specific character
    	if(colInfo.removeChar.length() > 0){
    		
    		this.logger.log("[REMOVE_CHAR]" + colInfo.removeChar, LogWriter.DEBUG);
    		
    		for(int i=0; i<colInfo.removeChar.length(); i++){        		
    			sourceText = Utility.replaceAll(sourceText, String.valueOf(colInfo.removeChar.charAt(i)), "");
        	}
    	}
    	
    	// RULE : Replacement
    	if(colInfo.replace.length() > 0){
    		
    		this.logger.log("[REPLACE] " + colInfo.replace, LogWriter.DEBUG);
    		
    		// Get full replace list
    		CSVEntity replaceList = new CSVEntity(colInfo.replace, MULTIPLE_REPLACE_ITEM_DELIMITER);
    		
    		// Replace item
    		CSVEntity replaceItem = null;
    		
    		for(int i=0; i<replaceList.getItemCount(); i++){
    			// Get each replace item
    			replaceItem = new CSVEntity(replaceList.getItemAt(i),REPLACE_ITEM_DELIMITER);
    			
    			// Replace only on str1, str2 exists
    			if(replaceItem.getItemCount() == 2){
    				sourceText = Utility.replaceAll(sourceText, replaceItem.getItemAt(0), replaceItem.getItemAt(1));
    			}
    		}
    	}
    	
    	// RULE : Multiple Value
    	if(colInfo.isMultipleVal){
    		
    		this.logger.log("[MULTIPLE_VALUE]", LogWriter.DEBUG);
    		this.logger.log("[MULTIPLE_VALUE_DELIMITER] " + colInfo.multipleValDelim, LogWriter.DEBUG);
    		
    		// Check delimiter is empty or not.
    		if(colInfo.multipleValDelim.trim().length() > 0){
    		
	    		// Get multiple value into CSV entity
	    		CSVEntity multiVal = new CSVEntity(sourceText, colInfo.multipleValDelim);
	    		
	    		// TRIM each value and re-combine
	    		for(int i=0; i<multiVal.getItemCount(); i++){
	    			multiVal.setItem(i, multiVal.getItemAt(i).trim());
	    		}
	    		
	    		sourceText = multiVal.toString();
    		}    		
    		
    	}
    	
    	this.printSepLine(LogWriter.DEBUG);
    	
    	return sourceText;
    	
    }
    
    public boolean isEmptyRow(String text, String delimiter){
    	if(Utility.replaceAll(text.trim(),delimiter," ").trim().length() == 0) return true;
    	else return false;
    }
    
    public void printSepLine(int logLevel){
    	this.logger.logPureText("\n--------------------------------------------------------------------------\n", logLevel);
    }

}